package org.medusa.core;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

//缺陷
//1 ok 考虑一下delay_do(类似delay_event，但是不需要dispatch，时间到了立刻就会执行
//2.ok EnterFrame bug:行走和停止同时出现在一个周期中

//1.evp暂停、恢复、停止
//{
//1-1.EVCODE_SHUTDOWN事件处理中：clean掉所有事件和所有timer，但是要考虑 内置事件需要重新绑定，还有各种index重置为0
//1-2.直接clear是否不合理？因为数据包的注册还是有用的,要不clear的权限交给用户吧,用户要clear那就重置index和内置事件
//1-3.queue是否要清理呢？还是要在close时全部处理完毕？还是让用户去控制clean？
//1-4.3的问题引申到AsyncConnect的接收和发送队列的处理与清除，发送队列还存在连接断开发不出去的情况
//}
//2.如果timer队列为0时自动停止线程，有需要时再次打开
//3. 延迟DispatchDelayEvent：虽然事件已经可以定义为delay，但是还是需要普通事件有一个delay机制
//4.EnterFrame模式:1.Close没有机会执行回调，进程就要退出 2.如果在渲染帧中执行了逻辑帧，当渲染已经退出，那么逻辑帧的Close事件回调就没有机会被推送
//5.EnterFrame需要考虑过度占用渲染frame时间片段的情况:EnterFrame增加一个时间参数超出这个时间会提前跳出逻辑帧
//6. evp打开与关闭使用引用计数

public class EventPump {

	private EventPump self = null;
	private Thread thread_evp = null;
	private Thread thread_timer = null;

	private AtomicBoolean running = null;
	private AtomicLong ref_count = null;

	private HashMap<Long, EventDef> event_def_map = new HashMap<Long, EventDef>();
	private HashMap<Long, TimerDef> timer_def_map = new HashMap<Long, TimerDef>();
	// private LinkedBlockingQueue<EventDes> event_queue = new
	// LinkedBlockingQueue<EventDes>();
	private Channel<EventDes> event_queue = new Channel<EventDes>();
	// 内部事件
	private long EVCODE_SHUTDOWN;
	private long EVCODE_ADD_EVENT;
	private long EVCODE_DEL_EVENT;
	private long EVCODE_ADD_TIMER;
	private long EVCODE_DEL_TIMER;
	private long EVCODE_MODIFY_TIMER;
	private long EVCODE_TIMER_CHECK;

	// 控制方式:
	boolean auto_push_event = true;

	// timer check need:
	private Iterator<Entry<Long, TimerDef>> it = null;
	private Entry<Long, TimerDef> entry = null;
	private TimerDef tm = null;

	// push event need:
	private EventDes ev = null;
	private EventDef ev_def = null;

	private String name;

	public EventPump(String name) {
		this(name, true);
	}

	public EventPump(String name, boolean auto_push_event) {
		this.self = this;
		this.name = name;
		this.auto_push_event = auto_push_event;
		this.running = new AtomicBoolean(false);
		this.ref_count = new AtomicLong(0);

		this.EVCODE_SHUTDOWN = this.alloc_event_code();
		this.EVCODE_ADD_EVENT = this.alloc_event_code();
		this.EVCODE_DEL_EVENT = this.alloc_event_code();
		this.EVCODE_ADD_TIMER = this.alloc_event_code();
		this.EVCODE_DEL_TIMER = this.alloc_event_code();
		this.EVCODE_MODIFY_TIMER = this.alloc_event_code();
		this.EVCODE_TIMER_CHECK = this.alloc_event_code();

		this.init_event();
		this.init_thread();
		this.start();
	}

	public void EnterFrame() {
		if (!this.auto_push_event) {
			// 1.检查timer
			this.check_all_timer();
			// 2.推动事件
			this.push_all_event(false);
		}
	}

	private void start() {
		if (this.auto_push_event) {
			if (!this.thread_evp.isAlive()) {
				this.thread_evp.start();
			}
		} else {
			this.set_state_run();
		}
	}

	public void Stop() {
		this.DispatchEvent(this.EVCODE_SHUTDOWN);
	}

	public void Join() {
		if (this.thread_evp != null) {
			try {
				this.thread_evp.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		if (this.thread_timer != null) {
			try {
				this.thread_timer.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	public long AddEventListener(EventCallback<Object[]> proc) {
		long ev_code = this.alloc_event_code();
		this.DispatchEvent(this.EVCODE_ADD_EVENT, ev_code, (long) 0, proc);
		return ev_code;
	}

	public long AddDelayEventListener(long delay_ms, EventCallback<Object[]> proc) {
		long ev_code = this.alloc_event_code();
		this.DispatchEvent(this.EVCODE_ADD_EVENT, ev_code, delay_ms, proc);
		return ev_code;
	}

	public void RemoveEventListener(long ev_code) {
		this.DispatchEvent(this.EVCODE_DEL_EVENT, ev_code);
	}

	public long AddTimerListener(long interval_ms, long number,
			EventCallback<Object[]> proc) {

		long ev_code = this.alloc_event_code();
		this.DispatchEvent(this.EVCODE_ADD_TIMER, ev_code, interval_ms, number,
				proc);
		return ev_code;
	}

	// 根据总时间去处理
	// public long AddTimerListener(long interval_ms, long count_time_ms,
	// EventCallback proc) {
	// return this.AddTimerListener(interval_ms, count_time_ms/interval_ms,
	// proc);
	// }

	public long AddTimerListener(long interval_ms, EventCallback<Object[]> proc) {
		return this.AddTimerListener(interval_ms, Long.MAX_VALUE, proc);
	}

	public void RemoveTimerListener(long ev_code) {
		this.DispatchEvent(this.EVCODE_DEL_TIMER, ev_code);
	}

	public void ModifyTimerListener(long ev_code, long interval_ms, long number) {
		this.DispatchEvent(this.EVCODE_MODIFY_TIMER, ev_code, interval_ms,
				number);
	}

	public void DelayRun(long delay_ms, EventCallback<Object[]> proc,
			Object... args) {
		long ev_code = this.AddDelayEventListener(delay_ms, proc);
		this.DispatchEvent(ev_code, args);
	}

	public void DispatchEvent(long ev_code, Object... args) {
		if (this.is_running())
			this.event_queue.Write(new EventDes(ev_code, args));
	}

	/*
	 * public void DelayDispatchEvent(long delay_ms, long ev_code, Object...
	 * args) { if (this.is_running()) this.event_queue.Write(new
	 * EventDes(ev_code, args)); }
	 */

	private void check_all_timer() {
		if (this.timer_def_map.size() <= 0 || !this.is_running())
			return;// false;
		this.it = this.timer_def_map.entrySet().iterator();
		while (this.it.hasNext() && this.is_running()) {
			this.entry = this.it.next();
			this.tm = this.entry.getValue();
			this.tm.check_run();
		}
		// return true;
	}

	private void push_all_event(boolean blocking) {
		while (self.is_running()) {
			if (!blocking) {
				if (this.event_queue.Size() <= 0)
					break;
			}
			ev = this.event_queue.Read();
			// if (ev.ev_code == this.EVCODE_SHUTDOWN) {
			// this.set_state_stop();
			// break;
			// } else {
			ev_def = this.event_def_map.get(ev.ev_code);
			if (ev_def != null)
				ev_def.run(ev.args);
			// }

		}
	}

	private long alloc_event_code() {
		return this.ref_count.addAndGet(1);
	}

	private boolean is_running() {
		return this.running.get();
	}

	private boolean set_state_run() {
		return this.running.compareAndSet(false, true);
	}

	private boolean set_state_stop() {
		return this.running.compareAndSet(true, false);
	}

	private EventDef add_event_define(long ev_code, EventCallback<Object[]> proc) {
		return this.add_delay_event_define(ev_code, 0, proc);
	}

	private EventDef add_delay_event_define(long ev_code, long delay_ms,
			EventCallback<Object[]> proc) {
		EventDef evdef = new EventDef(this, ev_code, delay_ms, proc);
		this.event_def_map.put(ev_code, evdef);
		return evdef;
	}

	private void remove_event_define(long ev_code) {
		EventDef tmev = this.event_def_map.remove(ev_code);
		if (tmev != null)
			tmev.removed();
	}

	private TimerDef add_timer_define(long ev_code, long interval_seconds,
			long number, EventCallback<Object[]> proc) {
		return this.add_timer_define(ev_code, interval_seconds, number, true,
				proc);
	}

	private TimerDef add_timer_define(long ev_code, long interval_seconds,
			long number, boolean self_manager, EventCallback<Object[]> proc) {

		// timer thread
		if (!self.thread_timer.isAlive()) {
			self.thread_timer.start();
		}

		TimerDef tmdef = new TimerDef(this, ev_code, interval_seconds, number,
				self_manager);
		this.event_def_map.put(ev_code, new EventDef(this, ev_code, 0, proc));
		this.timer_def_map.put(ev_code, tmdef);

		return tmdef;
	}

	private void remove_timer_define(long ev_code) {
		this.remove_event_define(ev_code);
		this.timer_def_map.remove(ev_code);
	}

	private void modify_timer_define(long ev_code, long interval_ms, long number) {
		TimerDef tm = this.timer_def_map.get(ev_code);
		if (tm != null) {
			tm.modify(interval_ms, number);
		}
	}

	private void init_event() {

		this.add_event_define(this.EVCODE_SHUTDOWN,
				new EventCallback<Object[]>() {

					@Override
					public void execute(Object[] args) {
						self.set_state_stop();
					}

					@Override
					public void removed() {
						// TODO Auto-generated method stub
						
					}
				});

		this.add_event_define(this.EVCODE_ADD_EVENT,
				new EventCallback<Object[]>() {
					@Override
					public void execute(Object[] args) {
						long ev_code = (Long) args[0];
						long delay_ms = (Long) args[1];
						@SuppressWarnings("unchecked")
						EventCallback<Object[]> proc = (EventCallback<Object[]>) args[2];

						self.add_delay_event_define(ev_code, delay_ms, proc);
					}

					@Override
					public void removed() {
						// TODO Auto-generated method stub
						
					}
				});

		this.add_event_define(this.EVCODE_DEL_EVENT,
				new EventCallback<Object[]>() {
					@Override
					public void execute(Object[] args) {
						long ev_code = (Long) args[0];

						self.remove_event_define(ev_code);
					}

					@Override
					public void removed() {
						// TODO Auto-generated method stub
						
					}
				});

		this.add_event_define(this.EVCODE_ADD_TIMER,
				new EventCallback<Object[]>() {
					@Override
					public void execute(Object[] args) {
						long ev_code = (Long) args[0];
						long interval_seconds = (Long) args[1];
						long number = (Long) args[2];
						@SuppressWarnings("unchecked")
						EventCallback<Object[]> proc = (EventCallback<Object[]>) args[3];

						self.add_timer_define(ev_code, interval_seconds,
								number, proc);
					}

					@Override
					public void removed() {
						// TODO Auto-generated method stub
						
					}

				});

		this.add_event_define(this.EVCODE_DEL_TIMER,
				new EventCallback<Object[]>() {
					@Override
					public void execute(Object[] args) {
						long ev_code = (Long) args[0];

						self.remove_timer_define(ev_code);
					}

					@Override
					public void removed() {
						// TODO Auto-generated method stub
						
					}
				});

		this.add_event_define(this.EVCODE_MODIFY_TIMER,
				new EventCallback<Object[]>() {
					@Override
					public void execute(Object[] args) {
						long ev_code = (Long) args[0];
						long interval_ms = (Long) args[1];
						long number = (Long) args[2];
						self.modify_timer_define(ev_code, interval_ms, number);
					}

					@Override
					public void removed() {
						// TODO Auto-generated method stub
						
					}
				});

		this.add_event_define(this.EVCODE_TIMER_CHECK,
				new EventCallback<Object[]>() {

					@Override
					public void execute(Object[] args) {
						self.check_all_timer();
						// ------------------
						// begin = new Date();
						// todo:...
						// end = new Date();
						// -------------------
						/*
						 * sleep_ms = 1000 - end.getTime() - begin.getTime(); if
						 * (sleep_ms < 0) sleep_ms = 0;
						 * 
						 * try { Thread.sleep(sleep_ms); } catch
						 * (InterruptedException e) { e.printStackTrace(); }
						 */

					}
					/*
					 * public boolean ToDo() { this.it =
					 * self.timer_def_map.entrySet().iterator(); while
					 * (this.it.hasNext()) { entry = this.it.next(); this.tm =
					 * this.entry.getValue(); this.tm.check_run(); } return
					 * true; }
					 */

					@Override
					public void removed() {
						// TODO Auto-generated method stub
						
					}

				});

	}

	private void init_thread() {
		this.thread_evp = new Thread(new Runnable() {
			@Override
			public void run() {
				self.set_state_run();
				System.err.println("[" + self.name
						+ "] -evp thread is work!");
				self.push_all_event(true);
				System.err.println("[" + self.name
						+ "] +evp thread is free!");
				self.set_state_stop();
			}
		});

		this.thread_timer = new Thread(new Runnable() {
			@Override
			public void run() {
				System.err.println("[" + self.name
						+ "] -timer thread is work!");
				while (self.is_running()) {
					try {
						self.DispatchEvent(self.EVCODE_TIMER_CHECK);
						Thread.sleep(50);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}// end: while
				System.err.println("[" + self.name
						+ "] +timer thread is free!");
			}

		});
	}

	private static class EventDef {
		private EventPump evp;
		private long ev_code;
		private long delay_ms = 0;
		private EventCallback<Object[]> proc_func;

		private long delay_ev_code = 0;
		TimerDef tm_def = null;// this.evp.timer_def_map.get(this.ev_code);

		private EventDef(EventPump evp, long ev_code, long delay_ms,
				EventCallback<Object[]> proc_func) {
			this.evp = evp;
			this.ev_code = ev_code;
			this.delay_ms = delay_ms;
			this.proc_func = proc_func;
			if (this.delay_ms > 0) {
				this.delay_ev_code = this.evp.alloc_event_code();
				this.tm_def = this.evp.add_timer_define(this.delay_ev_code,
						this.delay_ms, 0, false, this.proc_func);
			}
		}

		private void run(Object[] args) {
			if (this.proc_func != null) {
				if (this.delay_ms > 0) {
					this.tm_def.inc_ref();
				} else {
					this.proc_func.execute(args);
					if (this.evp.timer_def_map.containsKey(this.ev_code)) {
						TimerDef tmdef = this.evp.timer_def_map
								.get(this.ev_code);
						tmdef.dec_ref();
					}

				}
			}// end :if (this.proc_func != null)
		}

		private void removed() {
			if (this.proc_func != null)
				this.proc_func.removed();
		}

	}

	private static class TimerDef {
		private EventPump evp;
		private long ev_code = 0;

		private long interval_ms;
		private long number;
		private boolean self_manager;
		private long last_run_time = 0;

		private TimerDef(EventPump evp, long ev_code, long interval_ms,
				long number, boolean self_manager) {
			this.evp = evp;
			this.ev_code = ev_code;
			this.interval_ms = interval_ms;
			this.number = number;
			this.self_manager = self_manager;
			this.last_run_time = System.currentTimeMillis();
		}

		private void check_run() {
			if (this.number > 0) {
				long now = System.currentTimeMillis();

				if ((now - this.last_run_time) >= (this.interval_ms)) {
					this.last_run_time = now;
					this.evp.DispatchEvent(this.ev_code, this.number);
				}
			}
		}

		private void modify(long interval_ms, long number) {
			this.interval_ms = interval_ms;
			this.number = number;
			this.last_run_time = System.currentTimeMillis();
		}

		private void inc_ref() {
			if (this.number <= 0) { // if tm is disable, set last_run_time:now
				this.last_run_time = System.currentTimeMillis();
			}
			this.number++;
		}

		private void dec_ref() {
			if (this.number != Long.MAX_VALUE)
				this.number--;
			if (this.number == 0 && this.self_manager)
				this.evp.remove_timer_define(ev_code);
		}

	}

	private static class EventDes {
		private long ev_code = 0;
		private Object[] args = null;

		private EventDes(long ev_code, Object[] args) {
			this.ev_code = ev_code;
			this.args = args;
		}

	}

}
